import sys
import time
import serial
import serial.tools.list_ports
import platform
from collections import deque
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
from PyQt5.QtGui import QIcon, QTextCursor
from PyQt5.QtCore import QTimer, QThread, pyqtSignal
from Ui_serial import Ui_MainWindow
from dialog import mysetDialogUI
import warnings

try:
    import list_port  # 仅 Linux 平台下使用
except ImportError:
    list_port = None

warnings.filterwarnings("ignore", category=DeprecationWarning)

__version__ = '1.03'
__author__ = '阿离不吃橙子'

system_platform = platform.system()


# 串口接收线程
class SerialThread(QThread):
    data_received = pyqtSignal(bytes)

    def __init__(self, ser):
        super().__init__()
        self.ser = ser
        self.running = True

    def run(self):
        buffer = b''
        while self.running:
            try:
                if not self.ser or not self.ser.is_open:
                    break
                if self.ser.in_waiting:
                    data = self.ser.read(self.ser.in_waiting)
                    buffer += data
                    while b'\n' in buffer:
                        line, buffer = buffer.split(b'\n', 1)
                        self.data_received.emit(line + b'\n')
                else:
                    time.sleep(0.01)
            except serial.SerialException:
                break
            except Exception as e:
                print("串口接收错误：", e)
                break

    def stop(self):
        self.running = False
        self.quit()
        self.wait()


# 主窗口
class MyWidget(QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.setWindowIcon(QIcon('icon.jpg'))

        self.ser = None
        self.serial_thread = None
        self.hexflag = 0
        self.buffered_data = deque()
        self.ConnectDatalst = ["", 9600, 8, 'N', 1.0]

        self.dia = mysetDialogUI()
        self.init_ui()
        self.connect_signals()

        self.update_timer = QTimer(self)
        self.update_timer.timeout.connect(self.update_ui)
        self.update_timer.start(100)

    def init_ui(self):
        self.serialscan()

    def connect_signals(self):
        self.ui.btn_refresh.clicked.connect(self.serialscan)
        self.ui.action_setting.triggered.connect(self.showdialog)
        self.ui.action_about.triggered.connect(self.about)
        self.ui.action_exit.triggered.connect(self.close)
        self.ui.btn_connect.clicked.connect(self.connect_serial)
        self.ui.btr_send.clicked.connect(self.send_data)
        self.ui.btn_clear.clicked.connect(self.clear_data)
        self.ui.checkBox_HEX.stateChanged.connect(self.hex_show)

    def serialscan(self):
        current_ports = {self.ui.cb_device.itemText(i) for i in range(self.ui.cb_device.count())}
        new_ports = set()

        if system_platform != 'Linux' or not list_port:
            ports = serial.tools.list_ports.comports()
        else:
            ports = list_port.comports()

        for port in ports:
            device = port.device
            new_ports.add(device)
            if device not in current_ports:
                self.ui.cb_device.addItem(device)

        for port in current_ports:
            if port not in new_ports:
                index = self.ui.cb_device.findText(port)
                self.ui.cb_device.removeItem(index)

    def showdialog(self):
        if self.dia.exec_() == 1:
            print("设置值：", self.dia.ui.ComboBox_ctr.currentText())
        else:
            print("取消设置")

    def about(self):
        QMessageBox.information(self, '关于', f'串口助手 {__version__}\n作者：{__author__}')

    def connect_serial(self):
        if self.ser is None:
            self.ConnectDatalst = [
                self.ui.cb_device.currentText(),
                int(self.ui.cb_bond.currentText()),
                int(self.ui.cb_data.currentText()),
                {'无': 'N', '奇校验': 'O', '偶校验': 'E'}.get(self.ui.cb_check.currentText(), 'N'),
                float(self.ui.cb_stop.currentText())
            ]
            self.open_serial()
        else:
            self.close_serial()

    def open_serial(self):
        try:
            self.ser = serial.Serial(
                port=self.ConnectDatalst[0],
                baudrate=self.ConnectDatalst[1],
                bytesize=self.ConnectDatalst[2],
                parity=self.ConnectDatalst[3],
                stopbits=self.ConnectDatalst[4],
                timeout=1
            )
            self.serial_thread = SerialThread(self.ser)
            self.serial_thread.data_received.connect(self.buffer_data)
            self.serial_thread.start()
            self.ui.btn_connect.setText("断开连接（已连接）")
        except Exception as e:
            QMessageBox.warning(self, "警告", "连接失败！请检查串口设置！")
            print("连接错误：", e)

    def close_serial(self):
        if self.serial_thread and self.serial_thread.isRunning():
            self.serial_thread.stop()
        if self.ser:
            self.ser.close()
            self.ser = None
        self.ui.btn_connect.setText("连接")

    def send_data(self):
        if not self.ser:
            QMessageBox.warning(self, "警告", "请先连接串口！")
            return
        try:
            text = self.ui.edit_send.toPlainText()
            if self.hexflag == 1:
                encoded = bytes.fromhex(text.replace(" ", ""))
            else:
                encoded = text.encode('ascii')
            self.ser.write(encoded)
            print("发送数据：", text)
        except Exception as e:
            QMessageBox.warning(self, "警告", "发送失败！")
            print("发送错误：", e)

    def buffer_data(self, raw_data):
        self.buffered_data.append(raw_data)

    def update_ui(self):
        if not self.buffered_data:
            return
        cursor = self.ui.edit_recv.textCursor()
        while self.buffered_data:
            raw_data = self.buffered_data.popleft()
            try:
                if self.hexflag == 0:
                    text = raw_data.decode('utf-8', errors='ignore').strip()
                else:
                    text = ' '.join(f"0x{b:02x}" for b in raw_data)
                cursor.insertText(text + '\n')
            except Exception as e:
                print("UI 解码错误：", e)
        self.ui.edit_recv.setTextCursor(cursor)
        self.ui.edit_recv.ensureCursorVisible()

    def hex_show(self, state):
        self.hexflag = 1 if state == 2 else 0
        print("当前显示模式：", "16进制" if self.hexflag else "普通")

    def clear_data(self):
        self.ui.edit_recv.clear()


# 启动入口
if __name__ == '__main__':
    app = QApplication(sys.argv)
    try:
        window = MyWidget()
        window.show()
        sys.exit(app.exec_())
    except Exception as e:
        print("主程序错误：", e)
